/**
 * Copyright (c) 2017-2024 Nop Platform. All rights reserved.
 * Author: canonical_entropy@163.com
 * Blog:   https://www.zhihu.com/people/canonical-entropy
 * Gitee:  https://gitee.com/canonical-entropy/nop-entropy
 * Github: https://github.com/entropy-cloud/nop-entropy
 */
package com.gitee.wsl.struct.table

import kotlin.jvm.JvmOverloads
import kotlin.math.max
import kotlin.math.min

/**
 * 描述一个连续的区域。包含firstIndex和lastIndex
 */

class CellRange(
   var  firstRowIndex: Int,
   var  firstColIndex: Int,
   var  lastRowIndex: Int,
   var  lastColIndex: Int
) : Comparable<CellRange> {

    init {
        // 确保小的标号在前
        if (firstRowIndex > lastRowIndex) {
            val t = firstRowIndex
            firstRowIndex = lastRowIndex
            lastRowIndex = t
        }
        if (firstColIndex > lastColIndex) {
            val t = firstColIndex
            firstColIndex = lastColIndex
            lastColIndex = t
        }

        if (firstRowIndex < 0 && lastRowIndex >= 0) firstRowIndex = 0

        if (firstColIndex < 0 && lastRowIndex >= 0) firstColIndex = 0

        if (lastRowIndex < 0 && lastColIndex < 0) {
            throw IllegalArgumentException("ERR_TABLE_INVALID_CELL_POSITION,row:$lastRowIndex,col:$lastColIndex")
                /*.param(ARG_ROW_INDEX, lastRowIndex)
                .param(ARG_COL_INDEX, lastColIndex)*/
        }
    }

    @JvmOverloads
    fun toABString(rowAbs: Boolean = false, colAbs: Boolean = false): String {
        if (firstRowIndex == lastRowIndex && firstColIndex == lastColIndex) return CellPosition.toABString(
            firstRowIndex,
            firstColIndex,
            rowAbs,
            colAbs
        )

        return (CellPosition.toABString(firstRowIndex, firstColIndex, rowAbs, colAbs) + ':'
                + CellPosition.toABString(lastRowIndex, lastColIndex, rowAbs, colAbs))
    }

    fun toRCString(): String {
        if (isWholeRow) {
            return "R" + (firstRowIndex + 1) + ":R" + (lastRowIndex + 1)
        } else if (isWholeCol) {
            return "C" + (firstColIndex + 1) + ":C" + (lastColIndex + 1)
        }

        if (firstRowIndex == lastRowIndex && firstColIndex == lastColIndex) return CellPosition.toRCString(
            firstRowIndex,
            firstColIndex
        )

        return (CellPosition.toRCString(firstRowIndex, firstColIndex) + ":"
                + CellPosition.toRCString(lastRowIndex, lastColIndex))
    }

    fun toRelativeRCString(r: Int, c: Int): String {
        val sb = StringBuilder(16)
        val wholeRow = isWholeRow
        val wholeCol = isWholeCol

        if (!wholeCol) {
            sb.append('R')
            _appendIndex(sb, firstRowIndex - r)
        }
        if (!wholeRow) {
            sb.append('C')
            _appendIndex(sb, firstColIndex - c)
        }
        sb.append(':')

        if (!wholeCol) {
            sb.append('R')
            _appendIndex(sb, lastRowIndex - r)
        }

        if (!wholeRow) {
            sb.append('C')
            _appendIndex(sb, lastColIndex - c)
        }

        return sb.toString()
    }

    override fun toString(): String {
        return toABString()
    }

    val isWholeCol: Boolean
        get() = lastRowIndex < 0

    val isWholeRow: Boolean
        get() = lastColIndex < 0

    val endRowIndex: Int
        get() {
            if (lastRowIndex < 0) return -1
            return lastRowIndex + 1
        }

    val endColIndex: Int
        get() {
            if (lastColIndex < 0) return -1
            return lastColIndex + 1
        }

    val rowCount: Int
        get() {
            if (isWholeCol) return Int.MAX_VALUE
            return this.lastRowIndex - this.firstRowIndex + 1
        }

    val colCount: Int
        get() {
            if (isWholeRow) return Int.MAX_VALUE
            return this.lastColIndex - this.firstColIndex + 1
        }

    val isSingleCell: Boolean
        get() {
            if (isWholeRow || isWholeCol) return false
            return firstRowIndex == lastRowIndex && firstColIndex == lastColIndex
        }

    fun intersectWith(range: CellRange): Boolean {
        val minr = max(firstRowIndex.toDouble(), range.firstRowIndex.toDouble())
            .toInt()
        val maxr = min(lastRowIndex.toDouble(), range.lastRowIndex.toDouble())
            .toInt()
        if (minr > maxr) return false

        val minc = max(firstColIndex.toDouble(), range.firstColIndex.toDouble())
            .toInt()
        val maxc = min(lastColIndex.toDouble(), range.lastColIndex.toDouble())
            .toInt()
        return minc <= maxc
    }

    fun intersect(range: CellRange): CellRange? {
        if (contains(range)) return range

        if (range.contains(this)) return this

        val minr = max(firstRowIndex.toDouble(), range.firstRowIndex.toDouble())
            .toInt()
        val maxr = min(lastRowIndex.toDouble(), range.lastRowIndex.toDouble())
            .toInt()
        if (minr > maxr) return null

        val minc = max(firstColIndex.toDouble(), range.firstColIndex.toDouble())
            .toInt()
        val maxc = min(lastColIndex.toDouble(), range.lastColIndex.toDouble())
            .toInt()
        if (minc > maxc) return null

        return CellRange(minr, minc, maxr, maxc)
    }

    fun contains(range: CellRange): Boolean {
        return firstRowIndex <= range.firstRowIndex && firstColIndex <= range.firstColIndex && lastRowIndex >= range.lastRowIndex && lastColIndex >= range.lastColIndex
    }

    fun offset(rowOffset: Int, colOffset: Int): CellRange {
        if (isWholeRow) {
            return CellRange(
                this.firstRowIndex + rowOffset, 0,
                lastRowIndex + rowOffset, -1
            )
        }

        if (isWholeCol) {
            return CellRange(
                0,
                firstColIndex + colOffset, -1,
                lastColIndex + colOffset
            )
        }

        return CellRange(
            this.firstRowIndex + rowOffset,
            firstColIndex + colOffset,
            lastRowIndex + rowOffset,
            lastColIndex + colOffset
        )
    }

    fun containsCell(rowIndex: Int, colIndex: Int): Boolean {
        return this.firstRowIndex <= rowIndex && (this.lastRowIndex >= rowIndex
                ) && (this.firstColIndex <= colIndex) && (this.lastColIndex >= colIndex)
    }

    override fun compareTo(o: CellRange): Int {
        if (firstRowIndex > o.firstRowIndex) return 1
        if (firstRowIndex < o.firstRowIndex) return -1
        if (firstColIndex > o.firstColIndex) return 1
        if (firstColIndex < o.firstColIndex) return -1

        if (lastRowIndex > o.lastRowIndex) return 1
        if (lastRowIndex < o.lastRowIndex) return -1
        if (lastColIndex > o.lastColIndex) return 1
        if (lastColIndex < o.lastColIndex) return -1
        return 0
    }

    companion object {
        private const val serialVersionUID = -5723999944891774654L

        fun fromPosition(first: CellPosition, last: CellPosition): CellRange {
            return CellRange(
                first.rowIndex,
                first.colIndex,
                last.rowIndex,
                last.colIndex
            )
        }

        fun fromABString(str: String): CellRange? {
            if (str.isBlank()) return null

            val pos = str.indexOf(':')

            if (pos < 0) {
                val ref = CellPosition.fromABString(str)?:return null
                return CellRange(
                    ref.rowIndex,
                    ref.colIndex,
                    ref.rowIndex,
                    ref.colIndex
                )
            } else {
                val first = CellPosition.fromABString(str.substring(0, pos))?:return null
                val last = CellPosition.fromABString(str.substring(pos + 1))?:return null
                return CellRange(
                    first.rowIndex,
                    first.colIndex,
                    last.rowIndex,
                    last.colIndex
                )
            }
        }

        private fun _appendIndex(sb: StringBuilder, index: Int) {
            if (index != 0) {
                sb.append('[').append(index).append(']')
            }
        }
    }
}